/* 
 * File:   AutoAlbumMaker.cpp
 * Author: Claudio
 * 
 * Created on February 25, 2013, 1:38 AM
 */

#include "AutoAlbumMaker.h"
#include "Image.h"
namespace AAM {

    AutoAlbumMaker::AutoAlbumMaker() {
        regionsX = regionsY = 2;
        numRegions = regionsX*regionsY;
        histComp = new HistogramComparison(2, regionsX, regionsY);
        minNumberOfClusters = 6;
        selectPhotos = true;
        autoOrganizePhotos = true;
        allowCropping = false;
        maxNumberOfPhotosPerPage = 6;
        maxNumberOfPages = 15;
        idCounter = 0;
		pageWidth = 800;
		pageHeight = 600;
		selection = true;
    }

    AutoAlbumMaker::AutoAlbumMaker(const AutoAlbumMaker& orig) {
    }

    AutoAlbumMaker::~AutoAlbumMaker() {
    }

    void AutoAlbumMaker::setPageDimensions(int width, int height) {
		pageWidth = width;
		pageHeight = height;
    }

    void AutoAlbumMaker::setMaxNumberOfPages(int mnop) {
        this->maxNumberOfPages = mnop;
    }

    void AutoAlbumMaker::setMaxNumberOfPhotosInPage(int mnopip) {

    }

    void AutoAlbumMaker::generateAlbum() {


        //Load images and process exif
        vector<Imagens*> allImages = loadImagesAndProcessExif();

        //Image quality analysis => Media

        SimmilarityClustering scluster;
        scluster.setHistogramComparison(histComp);
        
        int similarityThreshold = 7000;
//        vector<Imagens*> selectedAllImages = allImages;
	if(selection){
			if(maxNumberOfPages > 0)
			{
					while(allImages.size()/maxNumberOfPhotosPerPage > maxNumberOfPages){

					//        discardTerribleImages(allImages);
						allImages = scluster.eliminateDuplicatesAndSelectBetter(allImages,similarityThreshold);
						similarityThreshold+=1000;
				}
			}else{
				//Just run once
								//        discardTerribleImages(allImages);
						allImages = scluster.eliminateDuplicatesAndSelectBetter(allImages,similarityThreshold);
			}
	}        
        //Grouping, selection and distribution => Document & Pages
        Document *d;
        if (autoOrganizePhotos) {
            //System decides where to put photos
            d = groupImages(allImages);
        } else {
            //In this case, user will manually define in which page each photo will appear

        }
		d->setPageDimensions(pageWidth, pageHeight);
        //Pages Layout => 
        doLayout(d);

        //Render
        renderer->renderAlbum(d);
        
        system("del /Q temp\\*.*");
    }

    Document* AutoAlbumMaker::groupImages(vector<Imagens*> allImages) {
        TimeClustering tcluster;

        
        vector<Event> evt = tcluster.doCluster(allImages);
        bool timeClusterWorks = false;
        bool simmilarityClusterWorks = false;
        Document *d = new Document();
        ofstream out2("temp/saidaSimilaridadeHoras.txt", ios_base::trunc);
        if (evt.size() >= minNumberOfClusters) {

            timeClusterWorks = true;
        }
        if (!timeClusterWorks) {
            SimmilarityClustering scluster;
            scluster.setHistogramComparison(histComp);
            evt = scluster.doCluster(allImages);
            if (evt.size() > minNumberOfClusters)
                simmilarityClusterWorks = true;
        }
        if (!timeClusterWorks && !simmilarityClusterWorks) {
            //We have tried. But there is not enough info to cluster photos
            //Let's simply divide them equally through the pages
            int nPages = allImages.size();
            int imagesPerPage = allImages.size() / nPages;
            int counter = 0;
            int presentPage=0;
            while(counter < allImages.size()){
//            for (int a = 0; a < nPages; a++) {
                out2 << endl << " ====== SESSION " << presentPage++ << " ======";
                Page *p = new Page();
                for (int m = 0; m < imagesPerPage && counter < allImages.size(); m++) {
                    out2 << endl << allImages[counter]->getFilename();
                    AAM::Imagens *img = allImages[counter];
                    Media *image = new Image(img);

                    p->addMedia(image);

                    counter++;
                }
                d->addPage(p);
                out2 << endl;
            }
        }
        if (timeClusterWorks) {
            for (int a = 0; a < evt.size(); a++) {
                Event e = evt[a];
//                vector<Imagens*> grouping;
                vector<Imagens*> selected;
                vector<Imagens*>::iterator it=e.getItBegin();
                while(it!=e.getItEnd()){
                    selected.push_back((*it));
                    it++;
                }
//                for (int m = e.getStart(); m <= e.getEnd(); m++) {
//                    grouping.push_back(allImages[m]);
//                }
//                //eliminate duplicates
//                vector<Imagens*> selected;
//                if (selectPhotos)
//                    selected = scluster.eliminateDuplicatesAndSelectBetter(grouping);
//                else
//                    selected = grouping;

                //Reorder, since clusters have no order
//                sort(selected.begin(), selected.end(), comparaHoraImagens);
                //generate pages
                int counter = 0;
                while (counter < selected.size()) {
                    Page *p = new Page();
                    out2 << endl << " ====== SESSION " << a << " ======";
                    for (int n = 0; counter < selected.size() && n < maxNumberOfPhotosPerPage; n++) {
                        out2 << endl << selected[counter]->getFilename();
                        selected[counter]->setSession(a);
                        //                toAlbum.push_back(selected[n]);

                        AAM::Imagens *img = selected[counter];
                        Media *image = new Image(img);

                        p->addMedia(image);
                        counter++;

                    }
                    d->addPage(p);
                }

                out2 << endl;
            }
            out2.close();

        }

        return d;
    }

    void AutoAlbumMaker::setImagePathList(vector<string> imagelist) {
        imageList = imagelist;
    }

    void AutoAlbumMaker::setRenderer(Renderer *h) {
        renderer = h;
    }

    void AutoAlbumMaker::setOutputFilename(string outFilename) {
        outputFilename = outFilename;
    }

    vector<Imagens*> AutoAlbumMaker::loadImagesAndProcessExif() {
        vector<Imagens*> allImages;
        for (int a = 0; a < imageList.size(); a++) {
            string commandGetExifDateTime = "exiv2.exe -q -Pv -g Exif.Photo.DateTimeOriginal ";
            commandGetExifDateTime.append(imageList[a]);
            commandGetExifDateTime.append(" > temp/outTemp.txt");
            system(commandGetExifDateTime.c_str());

            ifstream file("temp/outTemp.txt");
            string date, time;
            file >> date >> time;
            file.close();

            Imagens* newImage = new Imagens(imageList[a], date, time);
			
			if (selection) {
				string commandGetPriority = "visualfeatureextractor.exe -i ";
				commandGetPriority.append(imageList[a]);
				commandGetPriority.append(" > temp/outVisual.txt");
				system(commandGetPriority.c_str());

				ifstream file2("temp/outVisual.txt");
				int priority;
				file2 >> priority;
				file2.close();
				newImage->setPriority(priority);
			}
            allImages.push_back(newImage);
        }


        for (int a = 0; a < allImages.size(); a++) {

            IplImage *resized1 = cvCreateImage(cvSize(100, 100), IPL_DEPTH_8U, 3);
            IplImage* src = cvLoadImage(allImages[a]->getFilename().c_str());
            //            allImages[a]->setNumberOfDistances(allImages.size());

            cvResize(src, resized1);
            cvReleaseImage(&src);
            for (int regiao = 0; regiao < numRegions; regiao++) {
                CvHistogram* hist = histComp->calcHist(resized1, regiao);
                //                allHist[a][regiao] = hist;
                allImages[a]->addHistogram(hist);
            }
            cvReleaseImage(&resized1);
            //            int percent = 100 * a / size;
            //            if (a > 0) {
            //                cout << "\b\b\b\b";
            //            }
            //            if (percent >= 10)
            //                cout << "\b";
            //            cout << "[" << percent << "%]";
        }
        return allImages;

    }

    bool AutoAlbumMaker::comparaHoraImagens(Imagens* a, Imagens *b) {
        return a->getTime() < b->getTime();
    }

    void AutoAlbumMaker::doLayout(Document *d) {
        vector<Page*> pages = d->getPages();
        while (pages.size() > maxNumberOfPages && maxNumberOfPages > 0) {
            reduceEvents(pages);
        }
        d->setPages(pages);

        vector<Page*>::iterator pagesIt = pages.begin();
        int pageNumber = 0;
        while (pagesIt != pages.end()) {

            vector<MediaSetup*> media = (*pagesIt)->getMedia();
            vector<MediaSetup*>::iterator mediaIt = media.begin();
            stringstream inputPage;
            inputPage << "temp/page" << pageNumber << ".txt";
            string inputPageFilename = inputPage.str();

            ofstream out3(inputPageFilename.c_str(), ios_base::trunc);
            while (mediaIt != media.end()) {
                Media* m = (*mediaIt)->getMedia();
                Image* i = (Image*) m;
                Imagens *img = i->getImage();
                out3 << img->getPriority() << " " << img->getFilename() << endl;
                mediaIt++;
            }
            out3.close();
            stringstream outputPage;
            outputPage << "temp/page" << pageNumber++ << "Out.txt";
            string outputPageFilename = outputPage.str();
            stringstream command;
            command << "dafga.exe " << inputPageFilename << " -w " << d->getPageWidth() << " " << d->getPageHeight() << " > " << outputPageFilename;
            system(command.str().c_str());

            //Now it's time to open the output file and setup media
            ifstream f(outputPageFilename.c_str(), ios::in);
            if (f) {
                string image_name;
                int id;
                int posX;
                int posY;
                int width;
                int height;
                int newWidth;
                int newHeight;
                int priority;
                int counter = 0;
                while (f >> id >> image_name >> posX >> posY >> width >> height >> newWidth >> newHeight) {
                    media[id]->setPosX(posX);
                    media[id]->setPosY(posY);
                    media[id]->setWidth(width);
                    media[id]->setHeight(height);
                    Media* m = media[id]->getMedia();
                    Image* i = (Image*) m;
                    Imagens *img = i->getImage();
                    //                out3 << img->getPriority() << " " << img->getFilename() << endl;
                    if (((double) newWidth / (double) newHeight) != ((double) width / (double) height)) {
                        stringstream tempFilename;
                        tempFilename << "temp/temp" << idCounter << ".jpg";
                        stringstream executionString;
                        executionString << "autocropper.exe " << img->getFilename() << " " << newWidth << " " << newHeight << " " << tempFilename.str();
                        string autoCropExecution(executionString.str());
                        media[id]->setTempFilename(tempFilename.str());
                        system(autoCropExecution.c_str());
                        idCounter++;
                    }else{
                        media[id]->setTempFilename(img->getFilename());
                    }
                    //                string path = image_name;
                    //                IplImage *img = cvLoadImage(path.c_str());
                    //    //            facade->addImage(new DAF::Image(img->width, img->height, priority, img, path.c_str()));
                    //                DAF::Image *newImage = new DAF::Image(img->width, img->height, priority, path.c_str());
                    //                newImage->set_id(counter);
                    //                facade->addImage(newImage);
                    //                loadedImages.push_back(img);
                    //                counter++;
                }
                f.close();
            }

            pagesIt++;
        }


    }

    bool AutoAlbumMaker::compareSpace(Pontuation a, Pontuation b) {
        return a.emptySpace > b.emptySpace;
    }

    bool AutoAlbumMaker::comparePriority(Pontuation a, Pontuation b) {
        return a.time < b.time;
//        return a.prioritySum > b.prioritySum;
    }

    void AutoAlbumMaker::reduceEvents(vector<Page*> &vec) {
        //    vector<int> diffs;
        //    vector<int> sequences;
        //    
        //    int smallest = vec.size();
        //    int smallestIndex= 0;
        vector<Pontuation> order;
        for (int a = 1; a < vec.size(); a++) {
            //        int diff = abs(vec[a].getNumberOfMedia()-vec[a-1].getNumberOfMedia());
            int diff = maxNumberOfPhotosPerPage - vec[a - 1]->getNumberOfMedia();
            diff += maxNumberOfPhotosPerPage - vec[a]->getNumberOfMedia();
            //        sequences.push_back(diff);
            //        if(diff < smallest){
            //            smallest = diff;
            //            smallestIndex = a-1;
            //            }
            vector<MediaSetup*> media = vec[a - 1]->getMedia();
            vector<MediaSetup*>::iterator mediaIt = media.begin();
            int prioritySum = 0;
            int diffTime = 0;
            bool timeSet = false;
            while (mediaIt != media.end()) {
                Media* m = (*mediaIt)->getMedia();
                Image* i = (Image*) m;
                Imagens *img = i->getImage();
                prioritySum += img->getPriority();
                if(!timeSet){
                    diffTime -= img->getTime();
                    timeSet=true;
                }
                mediaIt++;
            }
            media = vec[a]->getMedia();
            mediaIt = media.begin();
            while (mediaIt != media.end()) {
                Media* m = (*mediaIt)->getMedia();
                Image* i = (Image*) m;
                Imagens *img = i->getImage();
                prioritySum += img->getPriority();
                if(timeSet){
                    diffTime += img->getTime();
                    timeSet=false;
                }

                mediaIt++;
            }

            Pontuation pont;
            pont.id1 = a - 1;
            pont.id2 = a;
            pont.emptySpace = diff;
            pont.prioritySum = prioritySum;
            pont.time = diffTime;
            if(diff>0)
                order.push_back(pont);

        }
//        sort(order.begin(), order.end(), compareSpace);
//        int end = 0;
//        int type = order.at(0).emptySpace;
//        while(end < order.size() && order.at(end).emptySpace == type ) {
//            end++;
//        }
//        sort(order.begin(), order.begin() + end, comparePriority);
         sort(order.begin(), order.end() , comparePriority);
        int pos = 0;
        vector<MediaSetup*> ms = vec[order.at(pos).id2]->getMedia();
        for (int a = 0; a < ms.size(); a++) {
            vec[order.at(pos).id1]->addMediaSetup(ms[a]);
        }
        vec.erase(vec.begin() + order.at(pos).id2);
    }
void AutoAlbumMaker::setSelection(bool selection){
	this->selection = selection;
	
}
}